/**
 * 对象操作工具类 (TypeScript)
 */

import { TomlArray, TomlObject, TomlValue } from '../types/TomlTypes';

export class ObjUtils {
  /**
   * 检查对象是否有指定属性
   */
  static hasProperty(obj: any, key: string): boolean {
    return Object.prototype.hasOwnProperty.call(obj, key);
  }

  /**
   * 获取对象的所有键
   */
  static getKeys(obj: any): string[] {
    return Object.keys(obj);
  }

  /**
   * 获取对象的所有值
   */
  static getValues(obj: any): any[] {
    return Object.values(obj);
  }

  /**
   * 获取对象的键值对
   */
  static getEntries(obj: any): [string, any][] {
    return Object.entries(obj);
  }

  /**
   * 深度获取嵌套对象的值
   */
  static getNestedValue(obj: TomlObject, path: string): TomlValue | undefined {
    const keys = path.split('.');
    let current: any = obj;

    for (const key of keys) {
      if (this.isObject(current) && this.hasProperty(current, key)) {
        current = current[key];
      } else {
        return undefined;
      }
    }

    return current;
  }

  /**
   * 深度设置嵌套对象的值
   */
  static setNestedValue(obj: TomlObject, path: string, value: TomlValue): void {
    const keys = path.split('.');
    const lastKey = keys.pop();

    if (!lastKey) {
      return;
    }

    let current: any = obj;

    for (const key of keys) {
      if (!this.hasProperty(current, key)) {
        current[key] = {};
      }

      const next = current[key];
      if (this.isObject(next)) {
        current = next;
      } else {
        throw new Error(`Cannot set property '${key}' on non-object value`);
      }
    }

    current[lastKey] = value;
  }

  /**
   * 检查值是否为原始类型
   */
  static isPrimitive(value: any): boolean {
    return typeof value === 'string' ||
      typeof value === 'number' ||
      typeof value === 'boolean' ||
      value instanceof Date;
  }

  /**
   * 检查值是否为数组
   */
  static isArray(value: any): boolean {
    return Array.isArray(value);
  }

  /**
   * 检查值是否为对象
   */
  static isObject(value: any): boolean {
    return typeof value === 'object' &&
      value !== null &&
      !Array.isArray(value) &&
      !(value instanceof Date);
  }

  /**
   * 深度克隆值
   */
  static deepClone(value: any): any {
    if (this.isPrimitive(value)) {
      return value instanceof Date ? new Date(value.getTime()) : value;
    }

    if (this.isArray(value)) {
      return value.map((item: any) => this.deepClone(item));
    }

    if (this.isObject(value)) {
      const cloned: any = {};
      for (const key in value) {
        if (this.hasProperty(value, key)) {
          cloned[key] = this.deepClone(value[key]);
        }
      }
      return cloned;
    }

    return value;
  }

  /**
   * 合并两个对象
   */
  static mergeObjects(target: any, source: any): any {
    const result = this.deepClone(target);

    for (const key in source) {
      if (this.hasProperty(source, key)) {
        const sourceValue = source[key];
        const targetValue = result[key];

        if (this.isObject(targetValue) && this.isObject(sourceValue)) {
          result[key] = this.mergeObjects(targetValue, sourceValue);
        } else {
          result[key] = this.deepClone(sourceValue);
        }
      }
    }

    return result;
  }

  /**
   * 将值转换为字符串表示
   */
  static valueToString(value: any, indent: number = 0): string {
    const spaces = ' '.repeat(indent);

    if (this.isPrimitive(value)) {
      if (typeof value === 'string') {
        return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
      }
      if (value instanceof Date) {
        return value.toISOString();
      }
      return String(value);
    }

    if (this.isArray(value)) {
      if (value.length === 0) {
        return '[]';
      }

      const items = value.map((item: any) => this.valueToString(item, indent + 2));
      return `[\n${spaces}  ${items.join(`,\n${spaces}  `)}\n${spaces}]`;
    }

    if (this.isObject(value)) {
      const entries = this.getEntries(value);
      if (entries.length === 0) {
        return '{}';
      }

      const pairs = entries.map(([key, val]) =>
      `${spaces}  ${key} = ${this.valueToString(val, indent + 2)}`
      );

      return `{\n${pairs.join(',\n')}\n${spaces}}`;
    }

    return String(value);
  }

  /**
   * 验证值的结构
   */
  static validateValue(value: any): boolean {
    if (this.isPrimitive(value)) {
      return true;
    }

    if (this.isArray(value)) {
      return value.every((item: any) => this.validateValue(item));
    }

    if (this.isObject(value)) {
      return this.getValues(value).every((val: any) => this.validateValue(val));
    }

    return false;
  }

  /**
   * 获取对象的所有键路径
   */
  static getKeyPaths(obj: any, prefix: string = ''): string[] {
    const paths: string[] = [];

    for (const key in obj) {
      if (this.hasProperty(obj, key)) {
        const fullPath = prefix ? `${prefix}.${key}` : key;
        paths.push(fullPath);

        const value = obj[key];
        if (this.isObject(value)) {
          paths.push(...this.getKeyPaths(value, fullPath));
        }
      }
    }

    return paths;
  }

  /**
   * 创建空的TOML对象
   */
  static createEmptyObject(): TomlObject {
    return {};
  }

  /**
   * 创建空的TOML数组
   */
  static createEmptyArray(): TomlArray {
    return [];
  }

  /**
   * 安全地获取对象属性
   */
  static safeGet(obj: any, key: string): any {
    return this.hasProperty(obj, key) ? obj[key] : undefined;
  }

  /**
   * 安全地设置对象属性
   */
  static safeSet(obj: any, key: string, value: any): void {
    obj[key] = value;
  }

  /**
   * 检查两个值是否相等（深度比较）
   */
  static deepEqual(a: any, b: any): boolean {
    if (a === b) {
      return true;
    }

    if (this.isPrimitive(a) || this.isPrimitive(b)) {
      return a === b;
    }

    if (this.isArray(a) && this.isArray(b)) {
      if (a.length !== b.length) {
        return false;
      }
      for (let i = 0; i < a.length; i++) {
        if (!this.deepEqual(a[i], b[i])) {
          return false;
        }
      }
      return true;
    }

    if (this.isObject(a) && this.isObject(b)) {
      const keysA = this.getKeys(a);
      const keysB = this.getKeys(b);

      if (keysA.length !== keysB.length) {
        return false;
      }

      for (const key of keysA) {
        if (!this.hasProperty(b, key) || !this.deepEqual(a[key], b[key])) {
          return false;
        }
      }
      return true;
    }

    return false;
  }
}
